﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Resources;
using System.IO;
using System.Xml;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;

namespace SilverlightSnoop
{
    /// <summary>
    /// Represents a class that holds a Predicate and 
    /// an Action, used to execute commands in the
    /// Silverlight environment. If you wish to implement 
    /// reusable commands, you may wish to derive from
    /// <see cref="AbstractDelegateCommand"/>.
    /// </summary>
    public class DelegateCommand : ICommand
    {
        /// <summary>
        /// Represents a predicate which determines whether
        /// the command can execute.
        /// </summary>
        protected Predicate<object> _CanExecute = null;

        /// <summary>
        /// Represents an action that is called when the command
        /// is executed.
        /// </summary>
        protected Action<Object> _Execute = null;

        /// <summary>Occurs when changes occur that affect whether the command should execute.</summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Creates a new instance of the DelegateCommand class. Takes
        /// a Predicate and Action as the CanExecute and Execute methods.
        /// </summary>
        public DelegateCommand()
        {

        }

        /// <summary>
        /// Creates a new instance of the DelegateCommand class. Takes
        /// a Predicate and Action as the CanExecute and Execute methods.
        /// </summary>
        /// <param name="execute">The Action to call to execute 
        /// the command.</param>
        public DelegateCommand(Action<Object> execute)
            : this(null, execute)
        {

        }

        /// <summary>
        /// Creates a new instance of the DelegateCommand class. Takes
        /// a Predicate and Action as the CanExecute and Execute methods.
        /// </summary>
        /// <param name="canExecute">The Predicate to evaluate to 
        /// determine if the command can execute.</param>
        /// <param name="execute">The Action to call to execute 
        /// the command.</param>
        public DelegateCommand(Predicate<Object> canExecute, Action<Object> execute)
        {
            _CanExecute = canExecute;
            _Execute = execute;
        }

        /// <summary>Defines the method that determines whether the command can execute in its current state.</summary>
        /// <returns>True if this command can be executed; otherwise, false.</returns>
        /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null. </param>
        public bool CanExecute(object parameter)
        {
            if (_Runner != null
                && _Runner.IsBusy)
                return false;

            if (_CanExecute == null)
                return true;

            return _CanExecute(parameter);
        }

        BackgroundWorker _Runner;

        /// <summary>Defines the method to be called when the command is invoked.</summary>
        /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null. </param>
        public void Execute(object parameter)
        {
            if (_Execute != null)
            {
                if (_Runner == null)
                {
                    _Runner = new BackgroundWorker();
                    _Runner.DoWork += RunExecuteAsBackgroundWorker;
                    _Runner.RunWorkerCompleted += RunExecuteCompleted;
                }
                _Runner.RunWorkerAsync(parameter);
                UpdateCanExecute();
            }
        }

        /// <summary>
        /// Executes the command in a background thread. Notifies RunExecuteCompleted
        /// when execution of the thread is complete.
        /// </summary>
        /// <param name="sender">The original sender of the event.</param>
        /// <param name="e">The parameters associated with this event.</param>
        private void RunExecuteAsBackgroundWorker(Object sender, DoWorkEventArgs e)
        {
            _Execute.Invoke(e.Argument);
        }

        /// <summary>
        /// Notifies the command to call the UpdateCanExecute method
        /// as the command has finished execution.
        /// </summary>
        /// <param name="sender">The original sender of the event.</param>
        /// <param name="e">The parameters associated with the BackgroundWorker.</param>
        private void RunExecuteCompleted(Object sender, RunWorkerCompletedEventArgs e)
        {
            UpdateCanExecute();
        }

        /// <summary>
        /// Raises the CanExecuteChanged event asynchronously.
        /// </summary>
        public void UpdateCanExecute()
        {
            System.Windows.Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                if (CanExecuteChanged != null)
                    CanExecuteChanged(this, new EventArgs());
            }));
        }

    }
  
    /// <summary>
    /// Represents a command with abstractly-defined CanExecute and
    /// Execute methods. Useful for when you wish to define commands
    /// with method bodies.
    /// </summary>
    public abstract class AbstractDelegateCommand : DelegateCommand
    {
        /// <summary>
        /// Initializes a new instance of the AbstractDelegateCommand class.
        /// </summary>
        public AbstractDelegateCommand()
        {
            this._CanExecute = new Predicate<Object>(CanExecute);
            this._Execute = new Action<object>(Execute);
        }

        /// <summary>
        /// Returns whether or not the command can execute at this point.
        /// </summary>
        /// <param name="param">The object to pass as a parameter to the command.</param>
        /// <returns>Returns true if it can execute, false otherwise.</returns>
        protected abstract bool CanExecute(Object param);

        /// <summary>
        /// Executes the command.
        /// </summary>
        /// <param name="param">The parameter to use while executing the command.</param>
        protected abstract void Execute(Object param);
    }

    public delegate void OnObjectCreatedEvent(Object sender, Object created);

    public class CreateTypeDelegateCommand : AbstractDelegateCommand
    {
        public event OnObjectCreatedEvent ObjectCreated;

        protected override bool CanExecute(object param)
        {
            return param is XapAssemblyType;
        }

        protected override void Execute(object param)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    try
                    {
                        XapAssemblyType xatType = (XapAssemblyType)param;

                        var newObject = xatType.GetNewTypeInstance();

                        if (ObjectCreated != null)
                            ObjectCreated(this, newObject);
                    }
                    catch (Exception ex)
                    {
                        StringBuilder builder = new StringBuilder();
                        builder.AppendLine(String.Format("Could not create an instance of {0}. The following exceptions occured:", (param as XapAssemblyType).Name));
                        builder.AppendLine();
                        Exception exception = ex;
                        while (exception != null)
                        {
                            builder.AppendLine(String.Format("Exception: {0}", exception.Message));
                            exception = exception.InnerException;
                        }
                        MessageBox.Show(builder.ToString());
                    }
                });
        }
    }


    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            _Xaps.Add(new Xap());
            this.treSnoopView.SelectedItemChanged += OnSelectedItemChanged;
            this.treSnoopView.ItemsSource = _Xaps;
            this.btnCreate.Command = this.CreateTypeCommand;
        }

        void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            CreateTypeCommand.UpdateCanExecute();
        }

        public DelegateCommand CreateTypeCommand
        {
            get
            {
                if (_CreateTypeCommand == null)
                {
                    _CreateTypeCommand = new CreateTypeDelegateCommand();
                    _CreateTypeCommand.ObjectCreated += OnObjectCreated;
                }
                return _CreateTypeCommand;
            }
        }
        private CreateTypeDelegateCommand _CreateTypeCommand;

        void OnObjectCreated(object sender, object e)
        {
            FrameworkElement element = e as FrameworkElement;
            if (element == null)
                return;

            TabItem toAdd = new TabItem();

            Button closeBtn = new Button();
            closeBtn.Height = 8;
            closeBtn.Width = 8;
            closeBtn.Margin = new Thickness(4);
            closeBtn.Click += OnCloseButtonClicked;
            closeBtn.Tag = toAdd;
            TextBlock headerText = new TextBlock();
            headerText.Text = e.GetType().ToString();
            headerText.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
            headerText.VerticalAlignment = System.Windows.VerticalAlignment.Center;

            StackPanel contents = new StackPanel();
            contents.Orientation = Orientation.Horizontal;
            contents.Children.Add(headerText);
            contents.Children.Add(closeBtn);

            toAdd.Header = contents;

            toAdd.Content = element;

            tabCurrentXAPs.Items.Add(toAdd);
        }

        void OnCloseButtonClicked(object sender, RoutedEventArgs e)
        {
            (sender as Button).Click -= OnCloseButtonClicked;
            tabCurrentXAPs.Items.Remove((sender as Button).Tag);
            (sender as Button).Tag = null;
        }

        
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //LoadWindow window = new LoadWindow();
            //window.Closed += OnOpenWindowClosed;
            //window.Show();

        }

        void OnOpenWindowClosed(object sender, EventArgs e)
        {
            //LoadWindow childWindow = sender as LoadWindow;
            //childWindow.Closed -= OnOpenWindowClosed;
            //if(!childWindow.DialogResult.Value)
            //    return;

            //WebClient client = new WebClient();
            //client.OpenReadCompleted += OnOpenReadCompleted;
            //client.OpenReadAsync(new Uri(childWindow.XapLocation, UriKind.RelativeOrAbsolute));
        }

        private void OnOpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            //try
            //{

            //    _Xaps.Add(xap);
            //}
            //catch (Exception ex)
            //{
                
            //}
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            //OpenFileDialog ofd = new OpenFileDialog();
            //ofd.Filter = "Silverlight *.xap files|*.xap";
            //if(!ofd.ShowDialog().Value)
            //    return;

            //var xap = SilverlightXapLoader.LoadXAP(ofd.File.OpenRead(), ofd.File.Name);

            //_Xaps.Add(xap);
        }

        ObservableCollection<Xap> _Xaps = new ObservableCollection<Xap>();

    }
}
